package com.mxx.common.utils;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.mxx.common.exception.CommentException;
import io.netty.util.internal.StringUtil;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.util.*;

/**
 * @Author itzgyw
 * @Date 2020-01-31
 * @Time 0:25
 * @Description:
 */
public final class Strings {

    public static final String NULL_STR = "";
    /**
     * 下划线
     */
    public static final char SEPARATOR = '_';

    public static boolean anyMatch(String source, String... matches) {
        if (matches.length > 0) {
            return Arrays.asList(matches).stream().anyMatch(p -> Objects.equals(source, p));
        }
        return false;
    }

    public static boolean anyMatch(Object source, Object... matches) {
        if (matches.length > 0) {
            for (Object match : matches) {
                if (Objects.equals(source, match)) {
                    return true;
                }
            }
        }
        return false;
    }

    public static boolean noneMatch(String source, String... matches) {
        if (matches.length > 0) {
            return Arrays.asList(matches).stream().noneMatch(p -> Objects.equals(source, p));
        }
        return false;
    }

    public static String toString(byte[] byteArray) {
        return new String(byteArray, CharsetUtil.CHARSET_UTF_8);
    }

    public static byte[] toByte(String data) {
        return data.getBytes(CharsetUtil.CHARSET_UTF_8);
    }

    public static byte[] encode(String data) {
        return encode(toByte(data));
    }

    public static String encodeToStr(String data) {
        return toString(encode(toByte(data)));
    }

    public static String encodeToStrNoWarp(String data) {
        return toString(encode(toByte(data))).replaceAll("[\\s*\t\n\r]", NULL_STR);
    }

    public static byte[] encode(byte[] data) {
        return Base64.getEncoder().encode(data);
    }

    public static byte[] encodeNoWarp(byte[] data) {
        byte[] noWarpByte = new byte[data.length];
        int count = 0;
        for (byte b : data) {
            if (b == 9 || b == 10 || b == 13) {
                count++;
                continue;
            }
            noWarpByte[noWarpByte.length] = b;
        }
        return Base64.getEncoder().encode(Arrays.copyOf(noWarpByte, noWarpByte.length - count));
    }

    public static String encodeToStr(byte[] data) {
        return toString(Base64.getEncoder().encode(data));
    }

    public static String encodeToStrNoWarp(byte[] data) {
        return encodeToStr(data).replaceAll("[\\s*\t\n\r]", NULL_STR);
    }

    public static byte[] decode(byte[] data) {
        return Base64.getDecoder().decode(data);
    }

    public static byte[] decode(String data) {
        return decode(toByte(data));
    }

    public static String url(String url, Map<?, ?> map) {
        if (map.isEmpty()) {
            return url;
        }
        StringBuilder builder = new StringBuilder(url);
        builder.append("?");
        for (Map.Entry<?, ?> entry : map.entrySet()) {
            builder.append(urlEncode(String.valueOf(entry.getKey())));
            builder.append("=");
            builder.append(urlEncode(String.valueOf(entry.getValue())));
            builder.append("&");
        }
        if (!map.isEmpty()) {
            builder.deleteCharAt(builder.length() - 1);
        }

        return builder.toString();
    }

    public static String get(String str) {
        return getDefVal(str, NULL_STR);
    }

    public static <T> T getDefVal(T t, T def) {
        return t == null ? def : t;
    }


    public static String urlEncode(String data) {
        return urlEncode(data, "UTF-8");
    }

    public static String urlEncode(String data, String charset) {
        try {
            return URLEncoder.encode(data, charset);
        } catch (Exception ex) {
            throw new CommentException(ex);
        }
    }

    public static boolean isNotEmpty(Object... objs) {
        return !isEmpty(objs);
    }

    public static boolean isNotEmpty(Object obj) {
        return !isEmpty(obj);
    }

    public static boolean isEmpty(Object... objs) {
        for (int index = 0; index < objs.length; index++) {
            Object obj = objs[index];
            if (isEmpty(obj)) {
                return true;
            }
        }
        return false;
    }

    public static boolean isEmpty(Object obj) {
        if (obj == null) {
            return true;
        } else if (obj instanceof String) {
            return ((String) obj).isEmpty();
        } else if (obj instanceof Collection) {
            return ((Collection) obj).isEmpty();
        } else if (obj instanceof Map) {
            return ((Map) obj).isEmpty();
        }
        return false;
    }


    public static boolean isNotEmpty(String... strs) {
        return !isEmpty(strs);
    }

    public static boolean isNotEmpty(String str) {
        return !isEmpty(str);
    }

    public static boolean isEmpty(String... strs) {
        for (int index = 0; index < strs.length; index++) {
            String str = strs[index];
            if (isEmpty(str)) {
                return true;
            }
        }
        return false;
    }

    public static boolean isEmpty(String str) {
        return str == null || str.isEmpty();
    }

    public static boolean isNotNull(Object obj) {
        return !isNull(obj);
    }

    public static boolean isNull(Object obj) {
        return obj == null;
    }


    public static String toString(Object obj) {
        return obj == null ? null : String.valueOf(obj);
    }

    public static String readThrowable(Throwable throwable) throws IOException {
        StringWriter writer = new StringWriter();
        throwable.printStackTrace(new PrintWriter(writer));
        writer.flush();
        writer.close();
        return writer.getBuffer().toString();
    }


    public static String trim(String str) {
        return (str == null ? NULL_STR : str.trim());
    }

    public static String signMap(Map map) {
        List<String> keys = new ArrayList<>(map.keySet());
        Collections.sort(keys);
        StringBuilder sb = new StringBuilder();

        for (String key : keys) {
            Object v = map.get(key);
            if (v instanceof Map || v instanceof Collection) {
                sb.append(JSON.toJSONString(v, SerializerFeature.MapSortField));
            } else {
                sb.append(v == null ? NULL_STR : v.toString());
            }
        }
        return sb.toString();
    }

    /**
     * 格式化文本, {} 表示占位符<br>
     * 此方法只是简单将占位符 {} 按照顺序替换为参数<br>
     * 如果想输出 {} 使用 \\转义 { 即可，如果想输出 {} 之前的 \ 使用双转义符 \\\\ 即可<br>
     * 例：<br>
     * 通常使用：format("this is {} for {}", "a", "b") -> this is a for b<br>
     * 转义{}： format("this is \\{} for {}", "a", "b") -> this is \{} for a<br>
     * 转义\： format("this is \\\\{} for {}", "a", "b") -> this is \a for b<br>
     *
     * @param template 文本模板，被替换的部分用 {} 表示
     * @param params   参数值
     * @return 格式化后的文本
     */
    public static String formatPlaceholder(String template, Object... params) {
        if (isEmpty(params) || isEmpty(template)) {
            return template;
        }
        return StrFormatter.format(template, params);
    }

    /**
     * 驼峰转下划线命名
     */
    public static String toUnderScoreCase(String str) {
        if (str == null) {
            return null;
        }
        StringBuilder sb = new StringBuilder();
        // 前置字符是否大写
        boolean preCharIsUpperCase = true;
        // 当前字符是否大写
        boolean curreCharIsUpperCase = true;
        // 下一字符是否大写
        boolean nexteCharIsUpperCase = true;
        for (int i = 0; i < str.length(); i++) {
            char c = str.charAt(i);
            if (i > 0) {
                preCharIsUpperCase = Character.isUpperCase(str.charAt(i - 1));
            } else {
                preCharIsUpperCase = false;
            }

            curreCharIsUpperCase = Character.isUpperCase(c);

            if (i < (str.length() - 1)) {
                nexteCharIsUpperCase = Character.isUpperCase(str.charAt(i + 1));
            }

            if (preCharIsUpperCase && curreCharIsUpperCase && !nexteCharIsUpperCase) {
                sb.append(SEPARATOR);
            } else if ((i != 0 && !preCharIsUpperCase) && curreCharIsUpperCase) {
                sb.append(SEPARATOR);
            }
            sb.append(Character.toLowerCase(c));
        }
        return sb.toString();
    }

    /**
     * 是否包含字符串
     *
     * @param str  验证字符串
     * @param strs 字符串组
     * @return 包含返回true
     */
    public static boolean inStringIgnoreCase(String str, String... strs) {
        if (str != null && strs != null) {
            for (String s : strs) {
                if (str.equalsIgnoreCase(trim(s))) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空，则返回空字符串。 例如：HELLO_WORLD->HelloWorld
     *
     * @param name 转换前的下划线大写方式命名的字符串
     * @return 转换后的驼峰式命名的字符串
     */
    public static String convertToCamelCase(String name) {
        StringBuilder result = new StringBuilder();
        // 快速检查
        if (name == null || name.isEmpty()) {
            // 没必要转换
            return "";
        } else if (!name.contains("_")) {
            // 不含下划线，仅将首字母大写
            return name.substring(0, 1).toUpperCase() + name.substring(1);
        }
        // 用下划线将原始字符串分割
        String[] camels = name.split("_");
        for (String camel : camels) {
            // 跳过原始字符串中开头、结尾的下换线或双重下划线
            if (camel.isEmpty()) {
                continue;
            }
            // 首字母大写
            result.append(camel.substring(0, 1).toUpperCase());
            result.append(camel.substring(1).toLowerCase());
        }
        return result.toString();
    }

    /**
     * 驼峰式命名法
     * 例如：user_name->userName
     */
    public static String toCamelCase(String s) {
        if (s == null) {
            return null;
        }
        if (s.indexOf(SEPARATOR) == -1) {
            return s;
        }
        s = s.toLowerCase();
        StringBuilder sb = new StringBuilder(s.length());
        boolean upperCase = false;
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);

            if (c == SEPARATOR) {
                upperCase = true;
            } else if (upperCase) {
                sb.append(Character.toUpperCase(c));
                upperCase = false;
            } else {
                sb.append(c);
            }
        }
        return sb.toString();
    }

    @SuppressWarnings("unchecked")
    public static <T> T cast(Object obj) {
        return (T) obj;
    }

    public static String moneyFormat(String money) {
        int index = money.indexOf(".");
        if (index == -1) {
            return money + ".00";
        }
        int difference = money.length() - index;
        if (difference == 1) {
            return money + "00";
        } else if (difference == 2) {
            return money + "0";
        } else if (difference == 3) {
            return money;
        }
        String suffix = money.substring(index + 1);
        Integer suffixIntVal = new Integer(suffix);
        if (suffixIntVal > 0) {
            suffixIntVal = new BigDecimal(money.substring(index + 1, index + 3)).add(new BigDecimal(1)).intValue();
            String prefix = money.substring(0, index);
            if (suffixIntVal < 10) {
                return prefix + ".0" + suffixIntVal;
            }
            if (suffixIntVal >= 10 && suffixIntVal < 100) {
                return prefix + "." + suffixIntVal;
            } else {
                return new BigDecimal(prefix).add(new BigDecimal(1)).toString() + ".00";
            }

        } else {
            return money.substring(0, index + 3);
        }
    }

    public static String join(CharSequence delimiter, CharSequence... elements) {
        return String.join(delimiter, elements);
    }

    public static String join(CharSequence delimiter, Iterable<? extends CharSequence> elements) {
        return String.join(delimiter, elements);
    }

    public static String join(String separator, Object[] array) {
        return array == null ? null : join(separator, array, 0, array.length);
    }

    public static String join(String separator, Object[] array, int startIndex, int endIndex) {
        if (array == null) {
            return null;
        } else {
            if (separator == null) {
                separator = "";
            }

            int noOfItems = endIndex - startIndex;
            if (noOfItems <= 0) {
                return "";
            } else {
                StringBuilder buf = newStringBuilder(noOfItems);
                if (array[startIndex] != null) {
                    buf.append(array[startIndex]);
                }

                for (int i = startIndex + 1; i < endIndex; ++i) {
                    buf.append(separator);
                    if (array[i] != null) {
                        buf.append(array[i]);
                    }
                }

                return buf.toString();
            }
        }
    }

    private static StringBuilder newStringBuilder(int noOfItems) {
        return new StringBuilder(noOfItems * 16);
    }
}
